/* Copyright (c) 2024 Huawei Technologies Co., Ltd.
 * This file is a part of the CANN Open Software.
 * Licensed under CANN Open Software License Agreement Version 1.0 (the "License").
 * Please refer to the License for details. You may not use this file except in compliance with the License.
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
 * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
 * See LICENSE in the root of the software repository for the full text of the License.
 * ===================================================================================================================*/

#include "mmpa_stub.h"
#include "mmpa/mmpa_api.h"

typedef int mmErrorMSg;

INT32 mmOpen(const CHAR *path_name, INT32 flags) {
  INT32 fd = HANDLE_INVALID_VALUE;

  if (NULL == path_name) {
    syslog(LOG_ERR, "The path name pointer is null.\r\n");
    return EN_INVALID_PARAM;
  }
  UINT32 tmp = (UINT32)flags;
  if ((0 == (tmp & (O_RDONLY | O_WRONLY | O_RDWR | O_CREAT))) && (flags != O_RDONLY)) {
    syslog(LOG_ERR, "The file open mode is error.\r\n");
    return EN_INVALID_PARAM;
  }

  fd = open(path_name, flags);
  if (fd < MMPA_ZERO) {
    syslog(LOG_ERR, "Open file failed, errno is %s.\r\n", strerror(errno));
    return EN_ERROR;
  }
  return fd;
}

INT32 mmOpen2(const CHAR *path_name, INT32 flags, MODE mode) {
  INT32 fd = HANDLE_INVALID_VALUE;

  if (NULL == path_name) {
    syslog(LOG_ERR, "The path name pointer is null.\r\n");
    return EN_INVALID_PARAM;
  }
  if (MMPA_ZERO == (flags & (O_RDONLY | O_WRONLY | O_RDWR | O_CREAT))) {
    syslog(LOG_ERR, "The file open mode is error.\r\n");
    return EN_INVALID_PARAM;
  }
  if ((MMPA_ZERO == (mode & (S_IRUSR | S_IREAD))) && (MMPA_ZERO == (mode & (S_IWUSR | S_IWRITE)))) {
    syslog(LOG_ERR, "The permission mode of the file is error.\r\n");
    return EN_INVALID_PARAM;
  }

  fd = open(path_name, flags, mode);
  if (fd < MMPA_ZERO) {
    syslog(LOG_ERR, "Open file failed, errno is %s.\r\n", strerror(errno));
    return EN_ERROR;
  }
  return fd;
}

INT32 mmClose(INT32 fd) {
  INT32 result = EN_OK;

  if (fd < MMPA_ZERO) {
    syslog(LOG_ERR, "The file fd is invalid.\r\n");
    return EN_INVALID_PARAM;
  }

  result = close(fd);
  if (EN_OK != result) {
    syslog(LOG_ERR, "Close the file failed, errno is %s.\r\n", strerror(errno));
    return EN_ERROR;
  }
  return EN_OK;
}

mmSsize_t mmWrite(INT32 fd, VOID *mm_buf, UINT32 mm_count) {
  mmSsize_t result = MMPA_ZERO;

  if ((fd < MMPA_ZERO) || (NULL == mm_buf)) {
    syslog(LOG_ERR, "Input parameter invalid.\r\n");
    return EN_INVALID_PARAM;
  }

  result = write(fd, mm_buf, (size_t)mm_count);
  if (result < MMPA_ZERO) {
    syslog(LOG_ERR, "Write buf to file failed, errno is %s.\r\n", strerror(errno));
    return EN_ERROR;
  }
  return result;
}

mmSsize_t mmRead(INT32 fd, VOID *mm_buf, UINT32 mm_count) {
  mmSsize_t result = MMPA_ZERO;

  if ((fd < MMPA_ZERO) || (NULL == mm_buf)) {
    syslog(LOG_ERR, "Input parameter invalid.\r\n");
    return EN_INVALID_PARAM;
  }

  result = read(fd, mm_buf, (size_t)mm_count);
  if (result < MMPA_ZERO) {
    syslog(LOG_ERR, "Read file to buf failed, errno is %s.\r\n", strerror(errno));
    return EN_ERROR;
  }
  return result;
}

INT32 mmMkdir(const CHAR *lp_path_name, mmMode_t mode) {
  INT32 t_mode = mode;
  INT32 ret = EN_OK;

  if (NULL == lp_path_name) {
    syslog(LOG_ERR, "The input path is null.\r\n");
    return EN_INVALID_PARAM;
  }

  if (t_mode < MMPA_ZERO) {
    syslog(LOG_ERR, "The input mode is wrong.\r\n");
    return EN_INVALID_PARAM;
  }

  ret = mkdir(lp_path_name, mode);

  if (EN_OK != ret) {
    syslog(LOG_ERR, "Failed to create the directory, the ret is %s.\r\n", strerror(errno));
    return EN_ERROR;
  }
  return EN_OK;
}

void *memCpyS(void *dest, const void *src, UINT32 count) {
  char *tmp = (char *)dest;
  char *s = (char *)src;

  if (MMPA_ZERO == count) {
    return dest;
  }

  while (count--) {
    *tmp++ = *s++;
  }
  return dest;
}

INT32 mmUnlink(const CHAR *filename) {
  return unlink(filename);
}
INT32 mmRmdir(const CHAR *lp_path_name) {
  INT32 ret;
  DIR *childDir = NULL;

  if (lp_path_name == NULL) {
    return EN_INVALID_PARAM;
  }
  DIR *dir = opendir(lp_path_name);
  if (dir == NULL) {
    return EN_INVALID_PARAM;
  }

  const struct dirent *entry = NULL;
  size_t bufSize = strlen(lp_path_name) + (size_t)(PATH_SIZE + 2); // make sure the length is large enough
  while ((entry = readdir(dir)) != NULL) {
    if ((strcmp(".", entry->d_name) == MMPA_ZERO) || (strcmp("..", entry->d_name) == MMPA_ZERO)) {
      continue;
    }
    CHAR *buf = (CHAR *)malloc(bufSize);
    if (buf == NULL) {
      break;
    }
    ret = memset_s(buf, bufSize, 0, bufSize);
    if (ret == EN_ERROR) {
      free(buf);
      buf = NULL;
      break;
    }
    ret = snprintf_s(buf, bufSize, bufSize - 1U, "%s/%s", lp_path_name, entry->d_name);
    if (ret == EN_ERROR) {
      free(buf);
      buf = NULL;
      break;
    }

    childDir = opendir(buf);
    if (childDir != NULL) {
      (VOID)closedir(childDir);
      (VOID)mmRmdir(buf);
      free(buf);
      buf = NULL;
      continue;
    } else {
      ret = unlink(buf);
      if (ret == EN_OK) {
        free(buf);
        continue;
      }
    }
    free(buf);
    buf = NULL;
  }
  (VOID)closedir(dir);

  ret = rmdir(lp_path_name);
  if (ret == EN_ERROR) {
    return EN_ERROR;
  }
  return EN_OK;
}

INT32 mmGetSystemTime(mmSystemTime_t *sysTime) {
  return ge::MmpaStub::GetInstance().GetImpl()->mmGetSystemTime(sysTime);
}

mmTimespec mmGetTickCount() {
  mmTimespec rts;
  struct timespec ts = {0};
  (void)clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
  rts.tv_sec = ts.tv_sec;
  rts.tv_nsec = ts.tv_nsec;
  return rts;
}

INT32 mmGetTid() {
  INT32 ret = (INT32)syscall(SYS_gettid);

  if (ret < MMPA_ZERO) {
    return EN_ERROR;
  }

  return ret;
}

INT32 mmAccess(const CHAR *path_name) {
  if (path_name == NULL) {
    return EN_INVALID_PARAM;
  }

  INT32 ret = access(path_name, F_OK);
  if (ret != EN_OK) {
    return EN_ERROR;
  }
  return EN_OK;
}

INT32 mmStatGet(const CHAR *path, mmStat_t *buffer) {
  if ((path == NULL) || (buffer == NULL)) {
    return EN_INVALID_PARAM;
  }

  INT32 ret = stat(path, buffer);
  if (ret != EN_OK) {
    return EN_ERROR;
  }
  return EN_OK;
}

INT32 mmGetFileSize(const CHAR *file_name, ULONGLONG *length) {
  if ((file_name == NULL) || (length == NULL)) {
    return EN_INVALID_PARAM;
  }
  struct stat file_stat;
  (void)memset_s(&file_stat, sizeof(file_stat), 0, sizeof(file_stat));  // unsafe_function_ignore: memset
  INT32 ret = lstat(file_name, &file_stat);
  if (ret < MMPA_ZERO) {
    return EN_ERROR;
  }
  *length = (ULONGLONG)file_stat.st_size;
  return EN_OK;
}

INT32 mmScandir(const CHAR *path, mmDirent ***entryList, mmFilter filterFunc,  mmSort sort)
{
  return ge::MmpaStub::GetInstance().GetImpl()->mmScandir(path, entryList, filterFunc, sort);
}

VOID mmScandirFree(mmDirent **entryList, INT32 count)
{
  while (count--) {
    free(entryList[count]);
  }
  free(entryList);
}

INT32 mmAccess2(const CHAR *pathName, INT32 mode)
{
  if (pathName == NULL) {
    return EN_INVALID_PARAM;
  }
  INT32 ret = access(pathName, mode);
  if (ret != EN_OK) {
    return EN_ERROR;
  }
  return EN_OK;
}

INT32 mmGetTimeOfDay(mmTimeval *timeVal, mmTimezone *timeZone)
{
  return ge::MmpaStub::GetInstance().GetImpl()->mmGetTimeOfDay(timeVal, timeZone);
}

INT32 mmRealPath(const CHAR *path, CHAR *realPath, INT32 realPathLen)
{
  INT32 ret = EN_OK;
  if ((path == NULL) || (realPath == NULL) || (realPathLen < MMPA_MAX_PATH)) {
    return EN_INVALID_PARAM;
  }
  const CHAR *ptr = realpath(path, realPath);
  if (ptr == NULL) {
    ret = EN_ERROR;
  }
  return ret;
}

//INT32 mmGetErrorCode()
//{
//  return 0;
//}

INT32 mmIsDir(const CHAR *fileName)
{
  if (fileName == nullptr) {
    return EN_ERR;
  }

  DIR *pDir = opendir (fileName);
  if (pDir != nullptr) {
    (void) closedir (pDir);
    return EN_OK;
  }
  return EN_ERR;
}

INT32 mmGetEnv(const CHAR *name, CHAR *value, UINT32 len)
{
  INT32 ret;
  UINT32 envLen = 0;
  if (name == NULL || value == NULL || len == MMPA_ZERO) {
    return EN_INVALID_PARAM;
  }

  CHAR *envPtr = getenv(name);
  if (envPtr == NULL) {
    return EN_ERROR;
  }

  UINT32 lenOfRet = (UINT32)strlen(envPtr);
  if (lenOfRet < (MMPA_MEM_MAX_LEN - 1)) {
    envLen = lenOfRet + 1;
  }

  if (envLen != MMPA_ZERO && len < envLen) {
    return EN_INVALID_PARAM;
  } else {
    ret = memcpy_s(value, len, envPtr, envLen);
    if (ret != EN_OK) {
      return EN_ERROR;
    }
  }

  return EN_OK;
}

INT32 mmDlclose(VOID *handle)
{
  if (handle == nullptr) {
    return 1;
  }
  return ge::MmpaStub::GetInstance().GetImpl()->DlClose(handle);
}

CHAR *mmDlerror()
{
  return ge::MmpaStub::GetInstance().GetImpl()->Dlerror();
}

INT32 mmDladdr(VOID *addr, mmDlInfo *info)
{
  info->dli_fname = "/tmp/mmDlInfo.txt";
  return 0;
}

VOID *mmDlopen(const CHAR *fileName, INT32 mode)
{
  return ge::MmpaStub::GetInstance().GetImpl()->DlOpen(fileName, mode);
}

VOID *mmDlsym(VOID *handle, const CHAR *funcName) {
 if (reinterpret_cast<uintptr_t>(handle) < 0x8000) {
   return nullptr;
 }
  return ge::MmpaStub::GetInstance().GetImpl()->DlSym(handle, funcName);
}

INT32 mmGetPid()
{
  return (INT32)getpid();
}

INT32 mmGetErrorCode(VOID)
{
  INT32 ret = (INT32)errno;
  return ret;
}

CHAR *mmGetErrorFormatMessage(mmErrorMSg errnum, CHAR *buf, mmSize size)
{
  if ((buf == NULL) || (size <= 0)) {
    return NULL;
  }
  return strerror_r(errnum, buf, size);
}

INT32 mmSetEnv(const CHAR *name, const CHAR *value, INT32 overwrite) {
  if ((name == nullptr) || (value == nullptr)) {
    return EN_INVALID_PARAM;
  }
  return setenv(name, value, overwrite);
}
